//------------------------------------------------------------
//  Filename: ./eth_mac_bd.sv
//   
//  Author  : wlduan@gmail.com
//  Revise  : 2020-12-03 12:28
//  Description: 
//   
//  Copyright (C) 2014, YRBD, Inc. 					      
//  All Rights Reserved.                                       
//-------------------------------------------------------------
//
`timescale 1ns/1ps
 
module eth_mac_bd #(
    parameter N_PORTS = 3,
    parameter ADDR_WIDTH = 10,
    parameter DATA_WIDTH = 32
)
( 
    input  logic       clk_i,  
    input  logic       rstn_i,  
     
    BDU_IF.Slave       lint_slave[N_PORTS]
);
//-------------------------------------------------------
BDU_IF lint_master(); 
//-------------------------------------------------------
logic [N_PORTS-1:0]   active_i; 
logic [N_PORTS-1:0]   active_q; 
logic [N_PORTS-1:0]   active_req_i; 
logic [N_PORTS-1:0]   active_rrvs; 
logic [N_PORTS-1:0]   active_prev; 
logic [N_PORTS-1:0]   active_mask; 
logic [N_PORTS-1:0]   active_expc; 
logic [N_PORTS-1:0]   active_int;
logic [N_PORTS-1:0]   active_we_i;
logic [2*N_PORTS-1:0] active_dreq; 
logic [2*N_PORTS-1:0] active_dxpc; 
//-------------------------------------------------------
assign active_rrvs  = ~active_prev - active_prev; 
assign active_mask  = active_rrvs + 1'b1; 
assign active_dreq  = {active_req_i,{active_mask&active_req_i}}; 
assign active_expc  = active_dxpc[0 +: N_PORTS]|active_dxpc[N_PORTS +: N_PORTS]; 
//-------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin 
    if(rstn_i == 0)begin 
        active_prev <= 'b0; 
    end 
    else begin 
        active_prev <= active_expc; 
    end 
end 
//-------------------------------------------------------
always_comb begin 
    active_dxpc = '0; 
    for(reg[7:0] i=0;i<2*N_PORTS;i++) begin 
        if(active_dreq[i]) begin 
            active_dxpc[i] = 1'b1; 
            break; 
        end 
    end 
end 
//-------------------------------------------------------
assign active_i = active_expc; 
//-------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin 
    if(rstn_i == 'b0) begin 
        active_q <= 'b0; 
    end 
    else begin 
        active_q <= active_int; 
    end 
end 
//-------------------------------------------------------
enum logic[7:0] {IDLE,WAIT_GNT,WAIT_VLD} cs,ns; 
//-------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin 
    if(rstn_i == 'b0)begin 
        cs <= IDLE; 
    end 
    else begin 
        cs <= ns; 
    end 
end 
//-------------------------------------------------------
always_comb begin 
    ns = cs; 
    active_int = active_q; 
    lint_master.req = 1'b0; 
    case(cs) 
        IDLE: begin 
            if(|active_req_i) begin 
                active_int = active_i; 
                lint_master.req = 1'b1; 
                ns = (lint_master.gnt) ? WAIT_VLD : WAIT_GNT; 
            end 
        end 
        WAIT_GNT: begin
            lint_master.req = 1'b1; 
            if(lint_master.gnt) ns = WAIT_VLD; 
        end 
        WAIT_VLD: begin 
            if(lint_master.rvalid) begin 
                if(|active_req_i) begin 
                    active_int = active_i; 
                    lint_master.req = 1'b1; 
                    ns = (lint_master.gnt) ? WAIT_VLD : WAIT_GNT; 
                end 
                else begin 
                    ns = IDLE; 
                end 
            end 
        end 
    endcase 
end 
//-------------------------------------------------------
logic [N_PORTS*ADDR_WIDTH -1:0] ports_addr; 
logic [N_PORTS*DATA_WIDTH -1:0] ports_wdata; 
//-------------------------------------------------------
genvar i;
generate  
    for(i=0;i<N_PORTS;i++) begin 
        assign active_req_i[i] = lint_slave[i].req; 
        assign active_we_i[i]  = lint_slave[i].we; 

        assign lint_slave[i].rdata  = (active_q[i])? lint_master.rdata : 'b0; 
        assign lint_slave[i].rvalid = (active_q[i])? lint_master.rvalid: 'b0; 
        assign lint_slave[i].gnt    =  active_int[i]&lint_master.gnt; 
        
        assign ports_addr [ADDR_WIDTH*i +: ADDR_WIDTH] = lint_slave[i].addr[ADDR_WIDTH-1:0]; 
        assign ports_wdata[DATA_WIDTH*i +: DATA_WIDTH] = lint_slave[i].wdata[DATA_WIDTH-1:0]; 
    end 
endgenerate
//-------------------------------------------------------
always_comb begin 
    lint_master.addr  = '0; 
    lint_master.wdata = '0;
    for(reg[7:0] i=0;i<N_PORTS;i++) begin 
        lint_master.addr  = lint_master.addr  |(ports_addr [ADDR_WIDTH*i +:ADDR_WIDTH]&{ADDR_WIDTH{active_int[i]}}); 
        lint_master.wdata = lint_master.wdata |(ports_wdata[DATA_WIDTH*i +:DATA_WIDTH]&{DATA_WIDTH{active_int[i]}}); 
    end 
end 
//-------------------------------------------------------
logic        CEN; 
logic        WEN; 
logic [ 5:0] A; 
logic [31:0] D; 
logic [31:0] Q; 
//-------------------------------------------------------
`ifndef ASIC 
    parameter DEEPTH = 2**(ADDR_WIDTH-2);

    logic[31:0] mem[DEEPTH-1:0]; 

    initial begin
        foreach(mem[i]) begin
            mem[i] = 64 + i;
        end
    end
    //-------------------------------------------------------
    always_ff @(posedge clk_i) if ((~CEN)&(~WEN)) mem[A] <= D     ; 
    always_ff @(posedge clk_i) if ((~CEN)&(WEN))  Q      <= mem[A];  
`else 
    S55NLLG1PH_X32Y2D32 
    mem 
    ( 
        .CLK ( clk_i ) , 
        .CEN ( CEN   ) , 
        .WEN ( WEN   ) , 
        .A   ( A     ) , 
        .0   ( D     ) , 
        .Q   ( Q     ) 
    ); 
`endif 
//-------------------------------------------------------
assign A   =  lint_master.addr[ADDR_WIDTH-1:2]; 
assign WEN = ~lint_master.we; 
assign D   =  lint_master.wdata[DATA_WIDTH-1:0]; 
assign CEN = ~lint_master.req; 

assign lint_master.we    = |(active_int&active_we_i); 
assign lint_master.gnt   = lint_master.req; 
assign lint_master.rdata = Q; 
//-------------------------------------------------------
always @(posedge clk_i,negedge rstn_i) begin
    if(rstn_i == 1'b0) begin 
        lint_master.rvalid <= 'b0; 
    end 
    else begin 
        lint_master.rvalid <= lint_master.req; 
    end 
end  

endmodule
  
